#pragma once

#include <string>
#include <list>
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <map>
#include <tuple>
#include <memory>
#include "util.h"
#include "Singleton.h"

/**
 * @brief 将日志级别level的日志写入到logger
 */
#define MYWEB_LOG_LEVEL(logger, level) \
    if(logger->getLevel() <= level) \
        LogEventWrap(LogEvent::ptr(new LogEvent(logger, level, \
                        __FILE__, __LINE__, GetElapsedMS() - logger->getCreateTime(), GetThreadId(),\
                time(0), GetThreadName()))).getSS()

#define MYWEB_LOG_DEBUG(logger) MYWEB_LOG_LEVEL(logger, LogLevel::DEBUG)
#define MYWEB_LOG_INFO(logger) MYWEB_LOG_LEVEL(logger, LogLevel::INFO)
#define MYWEB_LOG_WARN(logger) MYWEB_LOG_LEVEL(logger, LogLevel::WARN)
#define MYWEB_LOG_ERROR(logger) MYWEB_LOG_LEVEL(logger, LogLevel::ERROR)
#define MYWEB_LOG_FATAL(logger) MYWEB_LOG_LEVEL(logger, LogLevel::FATAL)

#define MYWEB_LOG_FMT_LEVEL(logger, level, fmt, ...) \
    if(logger->getLevel() <= level) \
        LogEventWrap(LogEvent::ptr(new LogEvent(logger, level, \
                        __FILE__, __LINE__, GetElapsedMS() - logger->getCreateTime(), GetThreadId(),\
                time(0), GetThreadName()))).getEvent()->format(fmt, __VA_ARGS__)

#define MYWEB_LOG_FMT_DEBUG(logger, fmt, ...) MYWEB_LOG_FMT_LEVEL(logger, LogLevel::DEBUG, fmt, __VA_ARGS__)
#define MYWEB_LOG_FMT_INFO(logger, fmt, ...) MYWEB_LOG_FMT_LEVEL(logger, LogLevel::INFO, fmt, __VA_ARGS__)
#define MYWEB_LOG_FMT_WARN(logger, fmt, ...) MYWEB_LOG_FMT_LEVEL(logger, LogLevel::WARN, fmt, __VA_ARGS__)
#define MYWEB_LOG_FMT_ERROR(logger, fmt, ...) MYWEB_LOG_FMT_LEVEL(logger, LogLevel::ERROR, fmt, __VA_ARGS__)
#define MYWEB_LOG_FMT_FATAL(logger, fmt, ...) MYWEB_LOG_FMT_LEVEL(logger, LogLevel::FATAL, fmt, __VA_ARGS__)

/**
 * 获取root主日志器
 */
#define MYWEB_LOG_ROOT() LoggerMgr::GetInstance()->getRoot()

/**
 * 获取name日志器
 */
#define MYWEB_LOG_NAME(name) LoggerMgr::GetInstance()->getLogger(name)

class LogLevel {
public:
    enum Level {
        UNKNOWN = 0,
        DEBUG = 1,
        INFO = 2,
        WARN = 3,
        ERROR = 4,
        FATAL = 5
    };

    static const char *ToString(LogLevel::Level level);
};

class Logger;

/**
 * @brief 构造函数
 * @param[in] logger 日志器
 * @param[in] level 日志级别
 * @param[in] file 文件名
 * @param[in] line 文件行号
 * @param[in] elapse 程序启动依赖的耗时(毫秒)
 * @param[in] thread_id 线程id
 * @param[in] fiber_id 协程id
 * @param[in] time 日志事件(秒)
 * @param[in] thread_name 线程名称
 */
class LogEvent {
public:
    typedef std::shared_ptr<LogEvent> ptr;

    LogEvent();

    LogEvent(std::shared_ptr<Logger> logger, LogLevel::Level level, const char *file, int32_t line, uint32_t elapse,
             uint32_t thread_id, uint64_t time, const std::string &thread_name)
            : m_file(file), m_line(line), m_elapse(elapse), m_threadId(thread_id), m_threadName(thread_name),
              m_time(time), m_logger(logger), m_level(level) {};

    const char *getFile() { return m_file; }

    int32_t getLine() { return m_line; }

    uint32_t getElapse() { return m_elapse; }

    uint32_t getThreadId() { return m_threadId; }

    uint64_t getTime() { return m_time; }

    std::string getContent() const { return m_ss.str(); }

    std::shared_ptr<Logger> getLogger() const { return m_logger; }

    std::stringstream &getSS() { return m_ss; }

    LogLevel::Level getLevel() { return m_level; }

    const std::string &getThreadName() const { return m_threadName; }

    void format(const char *fmt, ...);

    void format(const char *fmt, va_list args);

private:
    const char *m_file = nullptr; //文件名
    int32_t m_line = 0;           //行号
    uint32_t m_elapse = 0;        //程序启动到现在的毫秒数
    uint32_t m_threadId = 0;      //线程id
    std::string m_threadName;     //线程名
    uint64_t m_time;              //时间戳
    std::stringstream m_ss;       //字符串内容流
    std::shared_ptr<Logger> m_logger; //日志器
    std::string m_content;
    LogLevel::Level m_level;        //日志等级
};

/**
 *  %m 消息
 *  %p 日志级别
 *  %r 累计毫秒数
 *  %c 日志名称
 *  %t 线程id
 *  %n 换行
 *  %d 时间
 *  %f 文件名
 *  %l 行号
 *  %T 制表符
 *  %N 线程名称
 *
 *  默认格式 "%d{%Y-%m-%d %H:%M:%S}%T%t%T%N%T%F%T[%p]%T[%c]%T%f:%l%T%m%n"
 */

class LogFormatter {
public:
    typedef std::shared_ptr<LogFormatter> ptr;

    std::string format(std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event);

    std::ostream &format(std::ostream &ofs, std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event);

    LogFormatter(const std::string &pattern);

public:
    class FormatItem {
    public:
        typedef std::shared_ptr<FormatItem> ptr;

        virtual ~FormatItem() {};

        // 纯虚函数，该父类为虚类，不能实例化
        virtual void
        format(std::ostream &os, std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) = 0;
    };

    void init();

private:
    std::string m_pattern;
    std::vector<FormatItem::ptr> m_items;
    bool m_error = false; //解析是否出错
};

//日志输出地
class LogAppender {
public:
    typedef std::shared_ptr<LogAppender> ptr;

    LogAppender() = default;

    LogAppender(const LogLevel::Level& level, const LogFormatter::ptr& formatter)
            :m_level(level), m_formatter(formatter)
    {}

    virtual ~LogAppender() {};

    virtual void log(std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) = 0;

    void setFormatter(LogFormatter::ptr formatter) { m_formatter = formatter; }

    void setLevel(LogLevel::Level val) { m_level = val; }

    LogFormatter::ptr getFormatter() { return m_formatter; }

protected:
    LogLevel::Level m_level;
    LogFormatter::ptr m_formatter;
};

//日志器
class Logger : public std::enable_shared_from_this<Logger> {
    friend class LoggerManager;
public:
    typedef std::shared_ptr<Logger> ptr;

    Logger(const std::string &name = "default");

    void log(LogLevel::Level level, LogEvent::ptr event);

    void debug(LogEvent::ptr event);

    void info(LogEvent::ptr event);

    void warn(LogEvent::ptr event);

    void error(LogEvent::ptr event);

    void fatal(LogEvent::ptr event);

    void addAppender(LogAppender::ptr appender);

    void delAppender(LogAppender::ptr appender);

    LogLevel::Level getLevel() const { return m_level; }

    void setLevel(LogLevel::Level level) { m_level = level; }

    const std::string &getName() const { return m_name; }

    uint64_t getCreateTime() const { return m_createTime; }

private:
    /// 日志名称
    std::string m_name;
    /// 日志级别
    LogLevel::Level m_level;
    /// Mutex
    //MutexType m_mutex;
    /// 日志目标集合
    std::list<LogAppender::ptr> m_appenders;
    /// 日志格式器
    LogFormatter::ptr m_formatter;
    /// 主日志器
    Logger::ptr m_root;
    /// 创建时间（毫秒）
    uint64_t m_createTime;
};

class StdoutLogAppender : public LogAppender {
public:
    typedef std::shared_ptr<StdoutLogAppender> ptr;

    StdoutLogAppender() = default;

    StdoutLogAppender(const LogLevel::Level& level, const LogFormatter::ptr& formatter)
            :LogAppender(level, formatter)
    {}

    void log(std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) override;
};

class FileLogAppender : public LogAppender {
public:
    typedef std::shared_ptr<FileLogAppender> ptr;

    FileLogAppender(const std::string &filename);
    FileLogAppender(const std::string &filename, const LogLevel::Level& level, const LogFormatter::ptr& formatter)
            :LogAppender(level, formatter), m_filename(filename)
    {}

    void log(std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) override;

    //重新打开文件，文件打开成功返回true
    bool reopen();

private:
    std::string m_filename;
    std::ofstream m_filestream;
};

class LogEventWrap {
public:
    LogEventWrap(LogEvent::ptr event) : m_event(event) {};

    ~LogEventWrap() {
        m_event->getLogger()->log(m_event->getLevel(), m_event);
    }

    LogEvent::ptr getEvent() { return m_event; }

    std::stringstream &getSS() { return m_event->getSS(); }

private:
    LogEvent::ptr m_event;
};

/**
 * @brief 日志器管理类
 */
class LoggerManager {
public:

    LoggerManager();

    /**
     * @brief 获取日志器
     * @param[in] name 日志器名称
     */
    Logger::ptr getLogger(const std::string& name);

    /**
     * @brief 初始化
     */
    void init();

    /**
     * @brief 返回主日志器
     */
    Logger::ptr getRoot() const { return m_root;}

    /**
     * @brief 将所有的日志器配置转成YAML String
     */
    //std::string toYamlString();
private:
    /// 日志器容器
    std::map<std::string, Logger::ptr> m_loggers;
    /// 主日志器
    Logger::ptr m_root;
};

/// 日志器管理类单例模式
typedef Singleton<LoggerManager> LoggerMgr;

